package cool.mtc.minghe.service.service.sys.dict.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import cool.mtc.core.exception.ServiceException;
import cool.mtc.core.page.PageData;
import cool.mtc.core.util.StringUtil;
import cool.mtc.minghe.data.entity.sys.dict.DictDO;
import cool.mtc.minghe.data.model.base.Dict;
import cool.mtc.minghe.data.model.sys.dict.dto.DictDTO;
import cool.mtc.minghe.data.model.sys.dict.dto.DictListDTO;
import cool.mtc.minghe.data.model.sys.dict.form.DictForm;
import cool.mtc.minghe.data.model.sys.dict.query.DictQuery;
import cool.mtc.minghe.service.manager.sys.dict.DictManager;
import cool.mtc.minghe.service.manager.sys.dict.DictTypeManager;
import cool.mtc.minghe.service.mapper.sys.dict.DictMapper;
import cool.mtc.minghe.service.service.BaseServiceImpl;
import cool.mtc.minghe.service.service.sys.dict.DictService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author 明河
 */
@Service(value = "dictService")
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class DictServiceImpl extends BaseServiceImpl implements DictService, cool.mtc.web.component.dict.DictService {
    private final DictMapper dictMapper;

    private final DictManager dictManager;
    private final DictTypeManager dictTypeManager;

    /**
     * @param type 定义于注解@Dict，当type为空时，从字典表中取数据，当type不为空时从业务表中获取字典数据
     */
    @Override
    public Object getValueByTypeAndKey(String type, Object key) {
        if (null == key || 0 == key.toString().length()) {
            return null;
        }
        String keyStr = key.toString();
        return StringUtil.isEmpty(type) ? super.getDictByKey(keyStr) : super.getDictByTypeAndKey(type, keyStr);
    }

    @Override
    @Transactional
    public long insert(DictForm form) {
        DictDO dictDO = form.transToDO();
        this.handleCheckDictTypeIdExist(form.getDictTypeId());
        dictDO.setCreator(userId());
        dictMapper.insert(dictDO);
        this.clearDictCache(dictDO);
        return dictDO.getId();
    }

    @Override
    @Transactional
    public void update(DictForm form) {
        DictDO dictDO = dictMapper.selectById(form.getId());
        if (null == dictDO) {
            throw new ServiceException("dict.data.expired");
        }
        form.updateDO(dictDO);
        this.handleCheckDictTypeIdExist(form.getDictTypeId());
        dictMapper.updateById(dictDO);
        this.clearDictCache(dictDO);
    }

    /**
     * 检查字典类型是否存在
     */
    private void handleCheckDictTypeIdExist(long dictTypeId) {
        boolean exist = dictTypeManager.listDict().stream()
                .anyMatch(item -> item.getKey().equals(String.valueOf(dictTypeId)));
        if (exist) {
            return;
        }
        throw new ServiceException("dict.dict_type.non_exist");
    }

    @Override
    @Transactional
    public void delete(Long id) {
        if (null == id) {
            return;
        }
        DictDO dictDO = dictMapper.selectById(id);
        if (null == dictDO) {
            return;
        }
        this.handleCheckCanDelete(dictDO);
        dictMapper.deleteById(id);
        this.clearDictCache(dictDO);
    }

    /**
     * 检查是否可以删除
     */
    private void handleCheckCanDelete(DictDO dictDO) {
        if (Boolean.TRUE.equals(dictDO.getPreset())) {
            throw new ServiceException("preset.delete.not_allow");
        }
        // TODO 检查是否可以删除
    }

    /**
     * 清除字典的缓存
     */
    private void clearDictCache(DictDO dictDO) {
        dictManager.clearDictCacheByDictTypeId(dictDO.getDictTypeId());
        DictDTO dictDTO = dictMapper.getById(dictDO.getId());
        dictManager.clearDictCacheByDictTypeCode(dictDTO.getDictTypeCode());
    }

    @Override
    public PageData<DictListDTO> pageList(DictQuery query) {
        OrderItem[] items = {
                OrderItem.asc("DT.order_index"),
                OrderItem.asc("D.order_index"),
        };
        Page<DictListDTO> page = dictMapper.pageList(super.pageParam(items), query);
        return PageData.of(page.getRecords(), page.getTotal());
    }

    @Override
    public DictDTO info(Long id) {
        if (null == id) {
            return null;
        }
        return dictMapper.getById(id);
    }

    @Override
    public Map<String, List<Dict>> dictListByDictTypeCodes(List<String> dictTypeCodes) {
        Map<String, List<Dict>> map = new HashMap<>();
        dictTypeCodes.forEach(item -> map.put(item, dictManager.listDictByDictTypeCode(item)));
        return map;
    }

    @Override
    public List<Dict> dictListByDictTypeCode(String dictTypeCode) {
        return dictManager.listDictByDictTypeCode(dictTypeCode);
    }

    @Override
    public Map<String, List<Dict>> dictListByDictTypeIds(List<String> dictTypeIds) {
        Map<String, List<Dict>> map = new HashMap<>();
        dictTypeIds.forEach(item -> map.put(item, dictManager.listDictByDictTypeId(Long.parseLong(item))));
        return map;
    }

    @Override
    @Transactional
    public void saveWithUpdate(List<DictDO> dictList) {
        for (DictDO dictDO : dictList) {
            QueryWrapper<DictDO> wrapper = new QueryWrapper<>();
            wrapper.eq("dict_type_id", dictDO.getDictTypeId());
            wrapper.eq("code", dictDO.getCode());
            DictDO existDictDO = dictMapper.selectOne(wrapper);
            if (null == existDictDO) {
                dictMapper.insert(dictDO);
                continue;
            }
            BeanUtils.copyProperties(dictDO, existDictDO, "id", "dict_type_id", "code");
            dictMapper.updateById(existDictDO);
        }
    }

    @Override
    public Long getIdByDictTypeCodeAndCode(String dictTypeCode, String code) {
        return dictMapper.getIdByDictTypeCodeAndCode(dictTypeCode, code);
    }
}
